/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.ambimmort.ucagent.modules.server.channels;


import com.ambimmort.ucagent.modules.server.codec.UcProtocolCodecFactory;
import com.ambimmort.ucagent.modules.server.dpi.DPIEndPoint;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author 定巍
 */
public final class ClientConnection extends Thread {

    public ConnectionState connetionState;
    private IoSession session = null;
    private IoConnector connector = null;
    private EndPoint endPoint = null;
    private boolean shouldStop = false;
    private DPIEndPoint dpiEndPoint = null;
    
    public synchronized ConnectionState getConnetionState() {
        return connetionState;
    }

    public synchronized void setConnetionState(ConnectionState connetionState) {
        this.connetionState = connetionState;
    }

    public synchronized IoSession getSession() {
        return session;
    }

    public synchronized void setSession(IoSession session) {
        this.session = session;
    }

    public synchronized IoConnector getConnector() {
        return connector;
    }

    public synchronized void setConnector(IoConnector connector) {
        this.connector = connector;
    }

    public synchronized EndPoint getEndPoint() {
        return endPoint;
    }

    public synchronized void setEndPoint(EndPoint endPoint) {
        this.endPoint = endPoint;
    }

    public ClientConnection(DPIEndPoint dpiEndPoint, String host, int port, IoHandler handler) {
        this.dpiEndPoint = dpiEndPoint;
        connetionState = ConnectionState.DisConnected;
        this.endPoint = new EndPoint();
        this.endPoint.setHost(host);
        this.endPoint.setPort(port);
        connector = new NioSocketConnector();
        connector.setConnectTimeoutMillis(10000);
        connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new UcProtocolCodecFactory()));
        connector.setHandler(new ClientChannelReconnectHandler(this, handler));
    }

    @Override
    public void run() {
        while (!shouldStop) {
            if (connetionState == ConnectionState.DisConnected) {
                connect();
            } else {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    Logger.getLogger(ClientConnection.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
    
    public synchronized void stopMe(){
        shouldStop = true;
        if(this.session!=null)
        this.session.close(true);
    }

    public synchronized void connect() {
        connetionState = ConnectionState.Connecting;
    
        ConnectFuture future = connector.connect(new InetSocketAddress(this.endPoint.getHost(), this.endPoint.getPort()));

        boolean b = future.awaitUninterruptibly(3, TimeUnit.SECONDS);

        if (b && !future.isConnected()) {
            needReconnect();
        }
    }

    public synchronized void needReconnect() {
        connetionState = ConnectionState.DisConnected;
        session = null;
        this.dpiEndPoint.getDpiEndPointBean().setIsClientChannelConnected(false);
        this.dpiEndPoint.persistence();
        
    }

    public synchronized boolean send(Object obj) {
        if (session != null) {
            if (session.isConnected()) {
                session.write(obj);
                return true;
            }
        }
        return false;
    }


    @Override
    public String toString() {
        return "ConnectionPoint{" + "endPoint=" + endPoint + "," + session + '}';
    }

    public static class EndPoint {

        private String host;
        private int port;

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 67 * hash + (this.host != null ? this.host.hashCode() : 0);
            hash = 67 * hash + this.port;
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final EndPoint other = (EndPoint) obj;
            if ((this.host == null) ? (other.host != null) : !this.host.equals(other.host)) {
                return false;
            }
            if (this.port != other.port) {
                return false;
            }
            return true;
        }

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        @Override
        public String toString() {
            return "EndPoint{" + "host=" + host + ", port=" + port + '}';
        }

    }
    
    

}
